﻿#include <stdlib.h>
#include <string.h>

#include <debug.h>
#include <os_api.h>
#include <lwip/sockets.h>

#define TASK_STACK_SIZE (10 * 1024)
static char _task_stack[TASK_STACK_SIZE];

static void app_socket_domain_test()
{
    char srv[] = "iot.yuge-info.com";
    char buf[1600] = "6005"; // port 6005
    size_t sz;
    struct addrinfo addr_hints, *addr_res, *addr_rp;
    int sock = -1;
    int ret;

    fd_set rfds;
    struct timeval tv;
    int maxfd;

    LOG_PRINTF("%s: +", __func__);

    memset(&addr_hints, 0, sizeof(addr_hints));
    addr_hints.ai_family = AF_UNSPEC;
    addr_hints.ai_socktype = SOCK_STREAM;
    addr_hints.ai_flags = 0;
    addr_hints.ai_protocol = 0;
    ret = getaddrinfo(srv, buf, &addr_hints, &addr_res);
    if (ret != 0)
        goto clean;
    for (addr_rp = addr_res; addr_rp; addr_rp = addr_rp->ai_next)
    {
        sock = socket(addr_rp->ai_family, addr_rp->ai_socktype, addr_rp->ai_protocol);
        if (sock < 0)
            continue;
        LOG_PRINTF("%s: create socket OK, sock %d", __func__, sock);
        ret = connect(sock, addr_rp->ai_addr, addr_rp->ai_addrlen);
        if (!ret)
        {
            LOG_PRINTF("%s: connect OK", __func__);
            break;
        }
        LOG_PRINTF("%s: connect error, errno %d", __func__, lwip_getsockerrno(sock));
        close(sock);
        sock = -1;
    }
    freeaddrinfo(addr_res);

    if (sock < 0)
    {
        goto clean;
    }

    strcpy(buf, "abcdefg");
    sz = strlen(buf);
    ret = (int)send(sock, buf, sz, 0);
    if (ret != (int)sz)
    {
        LOG_PRINTF("%s: send error, ret %d, errno %d", __func__, ret, lwip_getsockerrno(sock));
    }
    else
    {
        LOG_PRINTF("%s: send OK", __func__);
    }

    memset(&tv, 0, sizeof(tv));
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    maxfd = sock;
    ret = select(maxfd + 1, &rfds, NULL, NULL, &tv);
    if (ret < 0)
    {
        LOG_PRINTF("%s: select error", __func__);
        goto clean;
    }
    else if (!ret)
    {
        LOG_PRINTF("%s: select timeout", __func__);
        goto clean;
    }

    ret = recv(sock, buf, sizeof(buf) - 1, 0);
    if (ret < 0)
    {
        LOG_PRINTF("%s: recv error, ret %d, errno %d", __func__, ret, lwip_getsockerrno(sock));
        goto clean;
    }
    else if (!ret)
    {
        LOG_PRINTF("%s: recv error, remote close", __func__);
        goto clean;
    }
    buf[ret] = '\0';

    LOG_PRINTF("%s: recv OK, data size %u", __func__, ret);
    LOG_PRINTF("%s: %s", __func__, buf);

clean:
    if (sock >= 0)
        close(sock);
    LOG_PRINTF("%s: -", __func__);
    return;
}

static void app_socket_ip_test()
{
    char srv[] = "1.116.61.155";
    int port = 6005;
    char buf[1600];
    size_t sz;
    struct sockaddr_in addr;

    int sock = -1;
    int ret, sockerrno;
    int flags;
    int optval;
    socklen_t optlen;

    fd_set rfds, wfds;
    struct timeval tv;
    int maxfd;

    LOG_PRINTF("%s: +", __func__);

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
        goto clean;
    LOG_PRINTF("%s: create socket OK, sock %d", __func__, sock);

    flags = fcntl(sock, F_GETFL, 0);
    fcntl(sock, F_SETFL, flags | O_NONBLOCK);

    memset(&addr, 0, sizeof(addr));
    addr.sin_len = sizeof(addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(srv);

    ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0)
    {
        sockerrno = lwip_getsockerrno(sock);
        if (sockerrno == EINPROGRESS)
        {
            LOG_PRINTF("%s: connecting", __func__);

            memset(&tv, 0, sizeof(tv));
            tv.tv_sec = 10;
            tv.tv_usec = 0;
            FD_ZERO(&wfds);
            FD_SET(sock, &wfds);
            maxfd = sock;
            ret = select(maxfd + 1, NULL, &wfds, NULL, &tv);
            if (ret < 0)
            {
                LOG_PRINTF("%s: select ERROR", __func__);
                goto clean;
            }
            else if (!ret)
            {
                LOG_PRINTF("%s: select timeout", __func__);
                goto clean;
            }
            optlen = sizeof(optval);
            ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, (socklen_t *)&optlen);
            if (ret || optval)
            {
                LOG_PRINTF("%s: ERROR", __func__);
                goto clean;
            }

            LOG_PRINTF("%s: connect OK", __func__);
        }
        else
        {
            LOG_PRINTF("%s: connect error, errno %d", __func__, sockerrno);
            goto clean;
        }
    }
    else
    {
        LOG_PRINTF("%s: connect OK", __func__);
    }

    strcpy(buf, "abcdefg");
    sz = strlen(buf);
    ret = (int)send(sock, buf, sz, 0);
    if (ret != (int)sz)
    {
        LOG_PRINTF("%s: send error, ret %d, errno %d", __func__, ret, lwip_getsockerrno(sock));
    }
    else
    {
        LOG_PRINTF("%s: send OK", __func__);
    }

    memset(&tv, 0, sizeof(tv));
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    maxfd = sock;
    ret = select(maxfd + 1, &rfds, NULL, NULL, &tv);
    if (ret < 0)
    {
        LOG_PRINTF("%s: select error", __func__);
        goto clean;
    }
    else if (!ret)
    {
        LOG_PRINTF("%s: select timeout", __func__);
        goto clean;
    }

    ret = recv(sock, buf, sizeof(buf) - 1, 0);
    if (ret < 0)
    {
        LOG_PRINTF("%s: recv error, ret %d, errno %d", __func__, ret, lwip_getsockerrno(sock));
        goto clean;
    }
    else if (!ret)
    {
        LOG_PRINTF("%s: recv error, remote close", __func__);
        goto clean;
    }
    buf[ret] = '\0';

    LOG_PRINTF("%s: recv OK, data size %u", __func__, ret);
    LOG_PRINTF("%s: %s", __func__, buf);

clean:
    if (sock >= 0)
        close(sock);
    LOG_PRINTF("%s: -", __func__);
    return;
}

/*************************************************************************
enum netconn_evt {
  NETCONN_EVT_RCVPLUS,
  NETCONN_EVT_RCVMINUS,
  NETCONN_EVT_SENDPLUS,
  NETCONN_EVT_SENDMINUS,
  NETCONN_EVT_CONNECTED,
  NETCONN_EVT_ACCEPTPLUS,
  NETCONN_EVT_ERROR_CLSD,
  NETCONN_EVT_ERROR_RST,
  NETCONN_EVT_ERROR_ABRT,
  NETCONN_EVT_CLOSE_WAIT,
  NETCONN_EVT_SENDACKED,
  NETCONN_EVT_CLOSE_NORMAL,
  NETCONN_EVT_ERROR
};
NETCONN_EVT_RCVPLUS //  内核侧，表示这个包内核接收了，送给应用的事件
NETCONN_EVT_RCVMINUS, //表示这个包已经被响应过了
NETCONN_EVT_SENDPLUS  //  内核侧，发包成功后给的事件，表示可以发送更多的包了
NETCONN_EVT_SENDMINUS,// 内核侧，发包异常后给的事件，表示需要停止应用发包了
NETCONN_EVT_CONNECTED  //  connect 的event
NETCONN_EVT_ACCEPTPLUS    //accept 的eventss
ERROR_CLSD, socket异常关闭event；
ERROR_RST,  tcp收到reset的event；
ERROR_ABRT,  tcp连接abort的event；
CLOSE_WAIT,  tcp连接被动关闭，收到FIN的event；
NETCONN_EVT_SENDACKED,  // 这个表明发送的数据已经收到多少个ack
NETCONN_EVT_CLOSE_NORMAL,//正常close了
***********************************************************************/
static void app_socket_cb(int s, int evt, u16_t len)
{
    LOG_PRINTF("%s: sock %d, evt 0x%x, len %u", __func__, s, evt, len);
}

static void app_socket_cb_test()
{
    char srv[] = "iot.yuge-info.com";
    char buf[1600] = "6005"; // port 6005
    size_t sz;
    struct addrinfo addr_hints, *addr_res, *addr_rp;
    int sock = -1;
    int ret, sockerrno;
    int flags, connecting = 0, connected = 0;

    int optval;
    socklen_t optlen;

    fd_set rfds, wfds;
    struct timeval tv;
    int maxfd;

    LOG_PRINTF("%s: +", __func__);

    memset(&addr_hints, 0, sizeof(addr_hints));
    addr_hints.ai_family = AF_UNSPEC;
    addr_hints.ai_socktype = SOCK_STREAM;
    addr_hints.ai_flags = 0;
    addr_hints.ai_protocol = 0;
    ret = lwip_getaddrinfo(srv, buf, &addr_hints, &addr_res);
    if (ret != 0)
        goto clean;
    for (addr_rp = addr_res; addr_rp; addr_rp = addr_rp->ai_next)
    {
        sock = lwip_socket_with_callback(addr_rp->ai_family, addr_rp->ai_socktype, addr_rp->ai_protocol, app_socket_cb);
        if (sock < 0)
            continue;

        LOG_PRINTF("%s: create socket OK, sock %d", __func__, sock);

        flags = lwip_fcntl(sock, F_GETFL, 0);
        lwip_fcntl(sock, F_SETFL, flags | O_NONBLOCK);

        ret = lwip_connect(sock, addr_rp->ai_addr, addr_rp->ai_addrlen);
        if (ret < 0)
        {
            sockerrno = lwip_getsockerrno(sock);
            if (sockerrno == EINPROGRESS)
            {
                LOG_PRINTF("%s: connecting", __func__);
                connecting = 1;
                break;
            }
            else
            {
                LOG_PRINTF("%s: connect error, errno %d", __func__, sockerrno);
                close(sock);
                sock = -1;
            }
        }
        else
        {
            LOG_PRINTF("%s: connect OK", __func__);
            connected = 1;
            break;
        }
    }
    lwip_freeaddrinfo(addr_res);

    if (connecting)
    {
        memset(&tv, 0, sizeof(tv));
        tv.tv_sec = 10;
        tv.tv_usec = 0;
        FD_ZERO(&wfds);
        FD_SET(sock, &wfds);
        maxfd = sock;
        ret = lwip_select(maxfd + 1, NULL, &wfds, NULL, &tv);
        if (ret < 0)
        {
            LOG_PRINTF("%s: select ERROR", __func__);
            goto clean;
        }
        else if (!ret)
        {
            LOG_PRINTF("%s: select timeout", __func__);
            goto clean;
        }
        optlen = sizeof(optval);
        ret = lwip_getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, (socklen_t *)&optlen);
        if (ret || optval)
        {
            LOG_PRINTF("%s: ERROR", __func__);
            goto clean;
        }

        LOG_PRINTF("%s: connect OK", __func__);
    }
    else if (!connected)
    {
        goto clean;
    }

    strcpy(buf, "abcdefg");
    sz = strlen(buf);
    ret = (int)lwip_send(sock, buf, sz, 0);
    if (ret != (int)sz)
    {
        LOG_PRINTF("%s: send error, ret %d, errno %d", __func__, ret, lwip_getsockerrno(sock));
    }
    else
    {
        LOG_PRINTF("%s: send OK", __func__);
    }

    memset(&tv, 0, sizeof(tv));
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    maxfd = sock;
    ret = lwip_select(maxfd + 1, &rfds, NULL, NULL, &tv);
    if (ret < 0)
    {
        LOG_PRINTF("%s: select error", __func__);
        goto clean;
    }
    else if (!ret)
    {
        LOG_PRINTF("%s: select timeout", __func__);
        goto clean;
    }

    ret = lwip_recv(sock, buf, sizeof(buf) - 1, 0);
    if (ret < 0)
    {
        LOG_PRINTF("%s: recv error, ret %d, errno %d", __func__, ret, lwip_getsockerrno(sock));
        goto clean;
    }
    else if (!ret)
    {
        LOG_PRINTF("%s: recv error, remote close", __func__);
        goto clean;
    }
    buf[ret] = '\0';

    LOG_PRINTF("%s: recv OK, data size %u", __func__, ret);
    LOG_PRINTF("%s: %s", __func__, buf);

clean:
    if (sock >= 0)
        lwip_close(sock);
    LOG_PRINTF("%s: -", __func__);
    return;
}

static void app_task(void *param)
{
    unsigned count = 0;
    unsigned test_step = 0;

    // wait device to make data connection, if do not wait, the first connection
    // may failed, but if you try to make connection again and again, it will success
    // later after device have made data connection.
    sleep(10); // 10 seconds

    while (1)
    {
        count++;
        LOG_PRINTF("%s test count %u", __func__, count);

        if (test_step == 0)
        {
            app_socket_domain_test();
        }
        else if (test_step == 1)
        {
            app_socket_ip_test();
        }
        else if (test_step == 2)
        {
            app_socket_cb_test();
        }
        test_step++;
        if (test_step >= 3)
            test_step = 0;

        sleep(30);
    }
}

int main()
{
    int ret;
    OSTaskRef task_ref; // because following task will never exit, so we do not use task handle, so we don't care it's handle

    ret = OSATaskCreate(&task_ref, _task_stack, TASK_STACK_SIZE, OS_APP_PRIORITY, "app", app_task, NULL);
    ASSERT(ret == OS_SUCCESS);
}
